package net.jalbum.filters;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.Vector;

import javax.swing.JPanel;

public class MultipleResizableObjectPanel extends ResizableObjectPanel {
	/**
	 * 
	 */
	private static final long serialVersionUID = -7820278572564980535L;
	protected MultipleResizableObjectControl multipleResizableObjectControl;
	protected String namePrefix = "Resizable Object ";

	// the counter only goes up to set the name for new objects. It does not go down when an object is removed and therefore it is not ...
	// ... suitable for counting the number of objects in the vector.
	protected int counter = 0;
	protected boolean outlineSelected = true;
	protected boolean paintOnlySelected = false;
	protected boolean applyFilter = false;
	protected boolean emptyClickCreatesNewObject = false;
	protected Vector<ResizableObject> objectVector;
	protected ResizableObject hoveredOverObject;

	public MultipleResizableObjectPanel() {
		this.name = "MultipleResizableObjectPanel";
		this.author = "David Fichtmueller";
		this.version = "1.0";
		this.description = "This is just for inheriting for other filters. This is not applieable.";
		this.prescale = false;
		this.postscale = false;

		this.multipleResizableObjectControl = new MultipleResizableObjectControl(this);
		this.objectVector = new Vector<ResizableObject>();
		this.resizableObject.setName(this.namePrefix + this.counter);
		this.counter++;
		this.hoveredOverObject = this.resizableObject;
		this.applyFilter = true;
	}

	@Override
	public JPanel getControls() {
		return this.multipleResizableObjectControl;
	}

	public void addObject() {
		if (!this.resizableObject.isInitial()) {
			this.resizableObject.setState(ResizableObject.NOTHING);

			this.resizableObject = new ResizableObject(this.width, this.height);
			this.resizableObject.setParentPanel(this);
			// new object is added on top of the others
			this.objectVector.add(0, this.resizableObject);
			this.resizableObject.setName(this.namePrefix + this.counter);
			this.multipleResizableObjectControl.adjustResizableObject(this.resizableObject);
			this.counter++;
		}
	}

	public boolean selectObject(final String name) {
		// go through vector
		for (int i = 0; i < this.objectVector.size(); i++) {
			// look for objects with the same name
			if (this.objectVector.get(i).getName().equals(name)) {
				// make found object to current Object
				this.resizableObject.setState(ResizableObject.NOTHING);
				this.resizableObject = this.objectVector.get(i);
				this.multipleResizableObjectControl.adjustResizableObject(this.resizableObject);
				return true;
			}
		}
		// show that no object with that name was found, the current object remains
		return false;
	}

	public boolean selectObject(final int index) {
		if (index < 0 || index >= this.objectVector.size()) {
			return false;
		} else {
			this.resizableObject.setState(ResizableObject.NOTHING);
			this.resizableObject = this.objectVector.get(index);
			this.multipleResizableObjectControl.adjustResizableObject(this.resizableObject);
			return true;
		}
	}

	public boolean removeObject() {
		// remove current object, jump to top object in stack
		// only remove object if it is not the last one
		if (this.objectVector.size() > 1) {
			this.objectVector.remove(this.resizableObject);
			this.resizableObject = this.objectVector.get(0);
			this.multipleResizableObjectControl.adjustResizableObject(this.resizableObject);
			this.renderPreview();
			return true;
		}
		return false;
	}

	public boolean removeObject(final int index) {
		// check if index is in allowed range and only remove object if it is not the last one
		if (index < 0 || index >= this.objectVector.size() || this.objectVector.size() > 1) {
			return false;
		} else {
			final ResizableObject removedObject = this.objectVector.get(index);
			this.objectVector.remove(index);
			if (removedObject == this.resizableObject) {
				// if the current object was removed, select the first one as new current object
				this.resizableObject = this.objectVector.get(0);
				this.multipleResizableObjectControl.adjustResizableObject(this.resizableObject);
			}
			this.renderPreview();
			return true;
		}
	}

	public boolean removeObject(final String name) {
		// only remove object if it is not the last one
		if (this.objectVector.size() > 1) {
			// go through vector
			for (int i = 0; i < this.objectVector.size(); i++) {
				// look for objects with the same name
				if (this.objectVector.get(i).getName().equals(name)) {
					final ResizableObject removedObject = this.objectVector.get(i);
					// remove found object
					this.objectVector.remove(i);
					if (removedObject == this.resizableObject) {
						// if the current object was removed, select the first one as new current object
						this.resizableObject = this.objectVector.get(0);
						this.multipleResizableObjectControl.adjustResizableObject(this.resizableObject);
					}
					this.renderPreview();
					return true;
				}
			}
			// show that no object with that name was found, the current object remains
			return false;
		} else {
			return false;
		}
	}

	@Override
	public void paint(final Graphics g) {
		if (this.showOutline) {
			if (this.paintOnlySelected) {
				this.resizableObject.paint(g);
			} else {
				for (int i = 0; i < this.objectVector.size(); i++) {
					this.objectVector.get(i).paint(g);
				}
				if (this.outlineSelected && !this.resizableObject.isInitial()) {
					this.paintOutline(g);
				}
			}
		}
	}

	protected void paintOutline(final Graphics g) {
		g.setColor(Color.YELLOW);
		g.drawRect((int) (this.resizableObject.getOutX1() * (this.width - 1)), (int) (this.resizableObject.getOutY1() * (this.height - 1)), (int) (this.resizableObject.getObjectWidth() * (this.width - 1)), (int) (this.resizableObject.getObjectHeight() * (this.height - 1)));
	}

	public BufferedImage renderPreview(final BufferedImage bi) {
		BufferedImage out = bi;
		// applyFilter-check to avoid null pointer exception at initiation
		if (this.applyFilter) {
			// reset buffered Image
			out = new BufferedImage(bi.getWidth(), bi.getHeight(), bi.getType());
			// transfer data from resizedImage
			out.setData(bi.getData());

			// go through vector and render image for each object in vector
			for (int i = 0; i < this.objectVector.size(); i++) {
				out = this.renderObject(this.objectVector.get(i), out);
			}
		}
		return out;
	}

	protected BufferedImage renderObject(final ResizableObject object, final BufferedImage bi) {
		// nothing here yet, but will be overwritten in inheriting classes
		return bi;
	}

	@Override
	public void keyPressed(final KeyEvent arg0) {
		if (arg0.getKeyCode() == KeyEvent.VK_INSERT) {
			this.addObject();
			this.multipleResizableObjectControl.checkButtons();
			this.renderPreview();
		} else if (arg0.getKeyCode() == KeyEvent.VK_DELETE) {
			this.removeObject();
			this.multipleResizableObjectControl.checkButtons();
			this.renderPreview();
		} else if (arg0.getKeyCode() == KeyEvent.VK_PAGE_DOWN) {
			this.previousObject();
		} else if (arg0.getKeyCode() == KeyEvent.VK_PAGE_UP) {
			this.nextObject();
		} else if (arg0.getKeyCode() == KeyEvent.VK_ALT || arg0.getKeyCode() == KeyEvent.VK_CONTROL) {
			// alt and controll key events are passed to all Objects
			for (int i = 0; i < this.objectVector.size(); i++) {
				this.objectVector.get(i).keyPressed(arg0);
			}
		} else {
			// everything else is passed to the current object
			if (this.resizableObject != null) {
				this.resizableObject.keyPressed(arg0);
			}
		}
	}

	@Override
	public void keyReleased(final KeyEvent arg0) {
		if (arg0.getKeyCode() == KeyEvent.VK_ALT || arg0.getKeyCode() == KeyEvent.VK_CONTROL) {
			// alt and controll key events are passed to all Objects
			for (int i = 0; i < this.objectVector.size(); i++) {
				this.objectVector.get(i).keyReleased(arg0);
			}
		} else {
			// everything else is passed to the current object
			if (this.resizableObject != null) {
				this.resizableObject.keyReleased(arg0);
			}
		}
	}

	public void previousObject() {
		// select the following object
		this.selectObject((this.objectVector.indexOf(this.resizableObject) + 1) % this.objectVector.size());
		this.renderPreview();
	}

	public void nextObject() {
		// select the previous object
		this.selectObject((this.objectVector.indexOf(this.resizableObject) + this.objectVector.size() - 1) % this.objectVector.size());
		this.renderPreview();
	}

	@Override
	public void mouseMoved(final MouseEvent event) {

		final int x = event.getX();
		final int y = event.getY();
		if (this.resizableObject != null) {
			if (this.resizableObject.isInitial()
					|| (x >= this.resizableObject.getOutX1() * (this.width - 1) - 5 && x <= this.resizableObject.getOutX2() * (this.width - 1) + 5 && y >= this.resizableObject.getOutY1() * (this.height - 1) - 5 && y <= this.resizableObject.getOutY2() * (this.height - 1) + 5)) {
				// pass the mouse position to the selected object, if it is initial or within its boundaries (plus 5 pixel of tolerance on each side)
				this.hoveredOverObject.setState(ResizableObject.NOTHING);
				this.hoveredOverObject = this.resizableObject;
				this.resizableObject.mouseMoved(event);
			} else {
				// check all the other objects, if it is not the currently selected one
				for (int i = 0; i < this.objectVector.size(); i++) {
					// go through all objects and check if clicked pixel belongs to one of them
					if (x >= this.objectVector.get(i).getOutX1() * (this.width - 1) - 5 && x <= this.objectVector.get(i).getOutX2() * (this.width - 1) + 5 && y >= this.objectVector.get(i).getOutY1() * (this.height - 1) - 5
							&& y <= this.objectVector.get(i).getOutY2() * (this.height - 1) + 5) {
						// pass the mouse position to the first object it is over
						this.hoveredOverObject.setState(ResizableObject.NOTHING);
						this.hoveredOverObject = this.objectVector.get(i);
						this.hoveredOverObject.mouseMoved(event);
						return;
					}
				}
				// if the mouse is above none of the other objects, the call gets passed to the selected object
				this.hoveredOverObject.setState(ResizableObject.NOTHING);
				this.hoveredOverObject = this.resizableObject;
				this.resizableObject.mouseMoved(event);
			}
		}
	}

	@Override
	public void mousePressed(final MouseEvent event) {

		final int x = event.getX();
		final int y = event.getY();

		if (this.resizableObject != null) {
			if (this.resizableObject.isInitial()
					|| (x >= this.resizableObject.getOutX1() * (this.width - 1) - 5 && x <= this.resizableObject.getOutX2() * (this.width - 1) + 5 && y >= this.resizableObject.getOutY1() * (this.height - 1) - 5 && y <= this.resizableObject.getOutY2() * (this.height - 1) + 5)) {
				// pass the pressed mouse to the selected object, if it is initial or within its boundaries (plus 5 pixel of tolerance on each side)
				this.resizableObject.mousePressed(event);
			} else {
				// check all the other objects, if it is not the currently selected one
				for (int i = 0; i < this.objectVector.size(); i++) {
					// go through all objects and check if clicked pixel belongs to one of them
					if (x >= this.objectVector.get(i).getOutX1() * (this.width - 1) - 5 && x <= this.objectVector.get(i).getOutX2() * (this.width - 1) + 5 && y >= this.objectVector.get(i).getOutY1() * (this.height - 1) - 5
							&& y <= this.objectVector.get(i).getOutY2() * (this.height - 1) + 5) {
						// select the first object within whose area the pixel is as the new current object;
						this.selectObject(i);
						this.resizableObject.mousePressed(event);
						return;
					}
				}
				// if none of the other objects is responsible for the clicked pixel, either a new Object is created or the call is passed to the already selected object
				if (this.emptyClickCreatesNewObject) {
					this.addObject();
					// setting the current position to the new object
					this.mousePressed(event);
					this.multipleResizableObjectControl.checkButtons();
					this.renderPreview();
				} else {
					// the call gets passed to the selected object
					this.resizableObject.mousePressed(event);
				}
			}
		}
	}

	@Override
	public void dispose() {
		super.dispose();
		this.resizableObject = null;
	}

	@Override
	public void mouseExited(final MouseEvent arg0) {
		this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
	}

	public String getNamePrefix() {
		return this.namePrefix;
	}

	public void setNamePrefix(final String namePrefix) {
		this.namePrefix = namePrefix;
	}

	public int getCounter() {
		return this.counter;
	}

	public void setCounter(final int counter) {
		this.counter = counter;
	}

	public boolean isOutlineSelected() {
		return this.outlineSelected;
	}

	public void setOutlineSelected(final boolean outlineSelected) {
		this.outlineSelected = outlineSelected;
	}

	public boolean isPaintOnlySelected() {
		return this.paintOnlySelected;
	}

	public void setPaintOnlySelected(final boolean paintOnlySelected) {
		this.paintOnlySelected = paintOnlySelected;
	}

	public boolean isApplyFilter() {
		return this.applyFilter;
	}

	public void setApplyFilter(final boolean applyFilter) {
		this.applyFilter = applyFilter;
	}

	public Vector<ResizableObject> getObjectVector() {
		return this.objectVector;
	}

	public void setObjectVector(final Vector<ResizableObject> objectVector) {
		this.objectVector = objectVector;
	}

	public ResizableObject getHoveredOverObject() {
		return this.hoveredOverObject;
	}

	public void setHoveredOverObject(final ResizableObject hoveredOverObject) {
		this.hoveredOverObject = hoveredOverObject;
	}
}
